package commons.ihm;

import algo_lignes_sites.data.junction.A_Junction;
import algo_lignes_sites.ihm.PanelCartoTocTools_ls;
import commons.Synchronizer;
import commons.drawing.I_Drawable;
import commons.drawing.UtilsDrawing;
import commons.drawing.UtilsDrawing.DrawQuality;
import commons.drawing.couche.I_Couche;
import commons.ihm.ParametresAffichageCarto.ModeAffichage;
import commons.ihm.ParametresAffichageCarto.TypeAffichage;
import commons.logs.Logger;
import commons.params_appli.ParamsAppli;
import commons.spatialindex.I_SpatialIndexable;
import commons.utils.UtilsGeom;
import commons.utils.UtilsTime;
import commons.utils.primitive_geom.Point_d;
import commons.utils.primitive_geom.Rect_d;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.util.ArrayList;

public class PanelCarto extends javax.swing.JPanel {

	// Pour eviter des niveaux de zoom problématiques
	private final double ECHELLE_MIN = 0.00000001;
	private final double ECHELLE_Max = 10;
	
    public PanelCarto(A_PanelCartoTocTools panel_Pere) {
        initComponents();
		this.panel_Pere = panel_Pere;

		pac = new ParametresAffichageCarto();
		UtilsTime.initTimerMoyenne(10);

		//Type d'affichage
		bModeZoomPlus = false;
		bDrawOnlySelection = false;
    }

    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        setBorder(javax.swing.BorderFactory.createLineBorder(new java.awt.Color(51, 153, 255), 5));
        setCursor(new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR));
        addMouseWheelListener(new java.awt.event.MouseWheelListener() {
            public void mouseWheelMoved(java.awt.event.MouseWheelEvent evt) {
                formMouseWheelMoved(evt);
            }
        });
        addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                formMouseClicked(evt);
            }
            public void mousePressed(java.awt.event.MouseEvent evt) {
                formMousePressed(evt);
            }
            public void mouseReleased(java.awt.event.MouseEvent evt) {
                formMouseReleased(evt);
            }
        });
        addComponentListener(new java.awt.event.ComponentAdapter() {
            public void componentResized(java.awt.event.ComponentEvent evt) {
                formComponentResized(evt);
            }
        });
        addMouseMotionListener(new java.awt.event.MouseMotionAdapter() {
            public void mouseDragged(java.awt.event.MouseEvent evt) {
                formMouseDragged(evt);
            }
            public void mouseMoved(java.awt.event.MouseEvent evt) {
                formMouseMoved(evt);
            }
        });

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
        this.setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 390, Short.MAX_VALUE)
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 290, Short.MAX_VALUE)
        );
    }// </editor-fold>//GEN-END:initComponents

	private A_PanelCartoTocTools panel_Pere;

	// Variable pour affichage dans un bitmap
	private Graphics offgc = null;
	private Image offscreen = null;
	private Dimension currentPanelDimension;

	// Parametre d'affichage (emprise zoom etc.)
	private ParametresAffichageCarto pac;
	private Rect_d rectEmprise_m;

	//Type d'affichage
	private boolean bModeZoomPlus; // affichage du rectangle de sélection
	private boolean bDrawOnlySelection; // affichage de la sélection uniquement

	// Objet highlighted
	private I_Drawable objetHighLighted;
	public void setObjetcHighLighted(I_Drawable o) {objetHighLighted=o;}


	public I_Drawable getObjectHighlighted() {
		return objetHighLighted;
	}


	public void setEmpriseDataToDisplay(Rect_d rectEmprise_m) {
		this.rectEmprise_m = rectEmprise_m;
	    zoomEmpriseData();
	}


	public void zoomEmpriseData() {
		if (rectEmprise_m == null) return;
		zoomThisEmprise (rectEmprise_m);
	}

	public void zoomThisObject (I_SpatialIndexable o) {
		Rect_d rectVisu_m = new Rect_d(o.getXMin(), o.getYMin(), o.getXMax(), o.getYMax());

		if (rectVisu_m.isPoint()) {
			double largeur = ParamsAppli.getParam_d("Autogene.LARGEUR_ZOOM_OBJECT_POINT");
			rectVisu_m.expand(largeur/2, largeur/2);
		} else {
			rectVisu_m.expand(1.2);
		}

		pac.extendsViewToRect(rectVisu_m, getSize().width, getSize().height);
		panel_Pere.panelCartoAskForNewZoom(pac.getEchelleVisualisation());
	}

	public void zoomThisEmprise (Rect_d rectVisu_m) {
		if (rectVisu_m==null) return;
		Rect_d rectZoom;
		if (rectVisu_m.isPoint())
			rectZoom = new Rect_d(rectVisu_m.xMin, rectVisu_m.yMin, ParamsAppli.getParam_d("Autogene.LARGEUR_ZOOM_OBJECT_POINT"));
		else
			rectZoom = rectVisu_m;

		pac.extendsViewToRect(rectZoom, getSize().width, getSize().height);
		panel_Pere.panelCartoAskForNewZoom(pac.getEchelleVisualisation());
	}

	public void zoomThisEmprise_px (Rect_d rectVisu_px) {
		// astuce : on inverse xMin et yMax car une fois convertie de px vers m, l'axe des ordonnées est inversé ...
		Rect_d rectVisu_m = new Rect_d (pac.X2x(rectVisu_px.xMin), pac.Y2y(rectVisu_px.yMax), pac.X2x(rectVisu_px.xMax), pac.Y2y(rectVisu_px.yMin));
		zoomThisEmprise (rectVisu_m);
	}

	
	public void setEchelleVisu(double newEchelle) {
		pac.changeEchelle(newEchelle);
		repaint();
	}

	public void modifyZoomByFactor (double factor) {
		pac.changeEchelle(pac.getEchelleVisualisation() * factor);
		repaint();
	}

	private void prepareForPainting() {
		Dimension newDimension = getSize();
		// Cette optimisation ne fait rien gagner :-(
		if (offscreen==null || !currentPanelDimension.equals(newDimension)) {
			Logger.log_DEBUG1("Panel Carto : CREATE IMAGE");
			currentPanelDimension = newDimension;
			offscreen = createImage(currentPanelDimension.width, currentPanelDimension.height);
			offgc = offscreen.getGraphics();
		}
	}

	public void repaintOnlySelection() {
		bDrawOnlySelection = true;
		repaint();
	}

	public void setDrawQuality (DrawQuality drawQuality) {
		pac.drawQuality = drawQuality;
	}

	public void setTypeAffichage (TypeAffichage typeAffichage) {
		pac.typeAffichage = typeAffichage;
	}

	@Override
	public void paint(Graphics g) {

		// on indique que le paint est en cours
		// bloquant une tentative de calcul qui ferait appel à Synchronizer.beginCalcul()
		Synchronizer.beginPaint();

		Graphics2D g2d = (Graphics2D)g;
		if (pac.drawQuality == DrawQuality.PRECISE)
			UtilsDrawing.Quality(g2d);
		else
			UtilsDrawing.Speed(g2d);

		if (bModeZoomPlus) {
			//System.out.println("PAINT with rect Zoom+");
			// on réécrit l'image déjà dessinée
			g2d.drawImage(offscreen, 0, 0, this);
			// et on rajoute le rectangle de zoom
			g2d.setColor(Color.BLACK);
			
			// on recalcule le rectangle dessiné, car on a public dragger depuis n'importe quel coin du rectangle
			int xMin = Math.min(xClic, xCurrent);
			int xMax = Math.max(xClic, xCurrent);
			int yMin = Math.min(yClic, yCurrent);
			int yMax = Math.max(yClic, yCurrent);

			g2d.drawRect(xMin, yMin, xMax-xMin, yMax-yMin);

		} else if (bDrawOnlySelection) {
			// Utilisé quand la sélection a changé sans autre modif d'affichage
			bDrawOnlySelection = false; // ca n'a besoin de fonctionner qu'une fois
			// on réécrit l'image déjà dessinée
			g2d.drawImage(offscreen, 0, 0, this);
			
			// et on rajoute la sélection
			pac.modeAffichage = ModeAffichage.SELECTION;
			drawCouches(g2d, panel_Pere.getVisibleCouches());
			
			// puis l'objet en surbrillance
			drawObjetHighLighted(g2d);

		} else {
			UtilsTime.startTimerMoyenne();
			UtilsTime.startTimer("DRAW");

			prepareForPainting();
			Graphics2D g2d_offScreen = (Graphics2D) offgc;
			clearScreen(g2d_offScreen);
			if (pac.drawQuality == DrawQuality.PRECISE)
				UtilsDrawing.Quality(g2d_offScreen);
			else
				UtilsDrawing.Speed(g2d_offScreen);

			// Dessin normal
			pac.modeAffichage = ModeAffichage.NORMAL;
			drawCouches(g2d_offScreen, panel_Pere.getVisibleCouches());
			g.drawImage(offscreen, 0, 0, this);

			// Dessin de la sélection
			// On n'enregistre pas le dessin de sélection dans l'image offscreen
			//  ce qui permet des reaffichages rapide si simple changement de sélection
			pac.modeAffichage = ModeAffichage.SELECTION;
			drawCouches(g2d, panel_Pere.getVisibleCouches());

			// Dessin de l'objet en surbrillance
			drawObjetHighLighted(g2d);

			UtilsTime.stopTimerMoyenne();
			UtilsTime.stopTimer("DRAW");
			Logger.log_DEBUG1(UtilsTime.getStringDuree("DRAW"));

		}

		// on indique que le repaint est terminé
		// débloquant une tentative de calcul
		Synchronizer.endPaint();
	}

	private void drawObjetHighLighted (Graphics2D g2d) {
		if (objetHighLighted == null) return;
		pac.modeAffichage = ModeAffichage.HIGHLIGHT;
		objetHighLighted.draw(g2d, pac);
	}

	private void drawCouches(Graphics2D g2d, ArrayList<I_Couche> vCouche) {
		for (I_Couche couche : vCouche)
			couche.draw(g2d, pac, pac.getRectView());
	}


	private void clearScreen(Graphics g) {
		//on efface l'écran avec un rectangle blanc
		g.setColor(Color.WHITE);
		g.fillRect(0, 0, getWidth(), getHeight());
	}

	
	// Zoom via molette souris
	private void formMouseWheelMoved(java.awt.event.MouseWheelEvent evt) {//GEN-FIRST:event_formMouseWheelMoved
		if (bModeZoomPlus)
			panel_Pere.stopModeZoomPlus();

		int nbScroll = evt.getWheelRotation();
		double newEchelle = pac.getEchelleVisualisation();
		if (nbScroll>0) {
			for (int i = 0; i < nbScroll; i++)
				newEchelle *= 0.8;
		} else {
		    for (int i = 0; i < -nbScroll; i++)
		        newEchelle /= 0.8;
		}
		// est-on allé trop loin dans le zoom ?
		if (newEchelle>=ECHELLE_Max || newEchelle<=ECHELLE_MIN || Double.isInfinite(newEchelle) || Double.isNaN(newEchelle))
			return;
			
		Point_d ptInvariant = new Point_d (pac.X2x(evt.getX()), pac.Y2y(evt.getY()));
		pac.changeEchelle(newEchelle, ptInvariant);
		panel_Pere.panelCartoAskForNewZoom(newEchelle);
	}//GEN-LAST:event_formMouseWheelMoved



	//----------- Gestion du drag
	
	Point_d ptView_whenClic;
	int xClic, yClic, xCurrent, yCurrent;
	public void setModeZoomPlus (boolean bMode) {bModeZoomPlus = bMode;}
	public boolean isModeZoomPlus () {return bModeZoomPlus;}

	private void formMousePressed(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_formMousePressed
		//System.out.println("mouse pressed");
		ptView_whenClic = pac.getPtView_m().getCopy();
		xClic = evt.getX ();
		yClic = evt.getY ();
	}//GEN-LAST:event_formMousePressed

	private void formMouseDragged(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_formMouseDragged
		//System.out.println("DRAG");
		xCurrent = evt.getX();
		yCurrent = evt.getY();

		if (bModeZoomPlus) {
			// simple re-dessin avec le rectangle de zoom
			repaint();

		} else {
			// sinon, c'est un PAN
			double new_xCenterView = ptView_whenClic.x - pac.LG2lg (xCurrent - xClic);
			double new_yCenterView = ptView_whenClic.y + pac.LG2lg (yCurrent - yClic);
			pac.changePtView(new Point_d(new_xCenterView, new_yCenterView));
			repaint ();
		}
	}//GEN-LAST:event_formMouseDragged

	private void formMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_formMouseClicked
		double distSearch_m = pac.LG2lg(5); // on recherche sur 5 pixel autour du clic
		double xMouse_m = pac.X2x(evt.getX());
		double yMouse_m = pac.Y2y(evt.getY());
		
		panel_Pere.selectFromXY(xMouse_m, yMouse_m, distSearch_m);
	}//GEN-LAST:event_formMouseClicked

	private void formComponentResized(java.awt.event.ComponentEvent evt) {//GEN-FIRST:event_formComponentResized
		//System.out.println("RESIZE");
		pac.changeTaillePanel(this.getWidth(), this.getHeight());
		// l'appel à paint se fait automatiquement
	}//GEN-LAST:event_formComponentResized

	private void formMouseReleased(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_formMouseReleased
		xCurrent = evt.getX();
		yCurrent = evt.getY();
		panel_Pere.panelCartoHasReleasedMouse(xClic, yClic, xCurrent, yCurrent);
	}//GEN-LAST:event_formMouseReleased

	private void formMouseMoved(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_formMouseMoved
		// On raffraichi les coordonnées x,y dans le panel_pere
		panel_Pere.showXY(pac.X2x(evt.getX()), pac.Y2y(evt.getY()));
		
		// Ainsi que le facteur dist (souris <-> centre du node highlight) / rayon de ce node
		//  uniquement dans le cas de l'algo ligne site
		if (panel_Pere instanceof PanelCartoTocTools_ls && objetHighLighted!=null && objetHighLighted instanceof A_Junction && ((A_Junction)objetHighLighted).isNode() ) {
			A_Junction node = ((A_Junction)objetHighLighted);
			double rayonNode = node.getRayonNodeFinal();
			double distSourisNode = Point_d.distPtPt(pac.X2x(evt.getX()), pac.Y2y(evt.getY()), node.x, node.y);
			
			if (rayonNode > 0) {
				panel_Pere.showFact_DistSourisNode_RayonNode(distSourisNode/rayonNode);
			}
		}
	}//GEN-LAST:event_formMouseMoved


    // Variables declaration - do not modify//GEN-BEGIN:variables
    // End of variables declaration//GEN-END:variables

}
